package com.geeguo.ebuilder.core.common.utils.worker;

import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class WorkerIdHelper {

    @Autowired
    private RedisCacheService redisCacheService;

    @Getter
    private static short workerId;

    // workId 位数
    public static final byte workerIdBits = 10;

    // workId 最大值
    private static final short maxWorkerId = ~(-1 << workerIdBits);

    // workerId 的过期时间
    private static final int EXPIRE_SECONDS = 7200;

    // 定时任务线程池，用于 workId 续期
    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    @PostConstruct
    public void init() {
        try {
            // 生成 workerId
            workerId = (short) generateWorkerId();
            log.info("成功获取workerId: {}", workerId);
            // 启动定时续期任务，每30分钟续期一次
            startRenewTask();
        } catch (Exception e) {
            log.error("初始化雪花ID生成器失败", e);
            throw new RuntimeException("初始化雪花ID生成器失败", e);
        }
    }

    /**
     * 生成 workerId
     */
    public long generateWorkerId() {
        // 最多尝试3次
        for (int i = 0; i < 3; i++) {
            try {
                // 自增计数器
                long id = redisCacheService.incr("ebuilder:workerId:counter", 1);
                // 取模确保不超过最大值
                id = (id - 1) % (maxWorkerId + 1);

                // 尝试占用该ID
                String nodeKey = String.format("ebuilder:workerId:node:%d", id);
                Boolean result = redisCacheService.setIfAbsent(nodeKey, "1", EXPIRE_SECONDS);
                if (result) {
                    return id;
                }

                // 未能占用ID，稍等重试
                try {
                    Thread.sleep(50 + (long) (Math.random() * 100));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取workId被中断", e);
                }
            } catch (Exception e) {
                log.error("获取workId时发生错误", e);
                // 最后一次尝试
                if (i == 2) {
                    throw new RuntimeException("获取workId时发生错误", e);
                }
            }
        }
        throw new RuntimeException("获取workId失败，超过最大尝试次数");
    }

    /**
     * 启动定时续期任务，防止 workerId 过期
     */
    private void startRenewTask() {
        // 每30分钟续期一次，过期时间设置为1小时
        scheduler.scheduleAtFixedRate(() -> {
            try {
                renewId(workerId);
                log.debug("成功续期workerId: {}", workerId);
            } catch (Exception e) {
                log.error("ID续期失败", e);
            }
        }, 30, 30, TimeUnit.MINUTES);
    }

    /**
     * 续期ID，防止过期
     */
    public void renewId(short workerId) {
        redisCacheService.expire(String.format("ebuilder:workerId:node:%d", workerId), EXPIRE_SECONDS);
    }
}